<?php

if (!defined('BASEPATH'))
    exit('No direct script access allowed');

require_once('phpass-0.3/PasswordHash.php');

define('PHPASS_HASH_STRENGTH', 8);
define('PHPASS_HASH_PORTABLE', FALSE);

define('STATUS_ACTIVATED', '1');
define('STATUS_NOT_ACTIVATED', '0');

/**
 * Tank_auth
 *
 * Authentication library for Code Igniter.
 *
 * @package		Tank_auth
 * @subpackage Library
 * @author		Ilya Konyukhov (http://konyukhov.com/soft/)
 * @version		1.0.7
 * @based on	DX Auth by Dexcell (http://dexcell.shinsengumiteam.com/dx_auth)
 * @license		MIT License Copyright (c) 2008 Erick Hartanto
 * @modify BeYourCyber 27/12/2553
 */
class Tank_auth {

    private $error = array();
    /**
     * initial
     * - เรียก config ขึ้นมา
     * - load library session
     * - load การทำงานของ database
     * - load model tank_auth/users (ใช้สำหรับการ authen data)
     * - เรียกการทำงาน function autologin
     * @link autologin()
     */
    function __construct() {
        $this->ci = & get_instance();

        $this->ci->load->config('tank_auth', TRUE);

        $this->ci->load->library('session');
        $this->ci->load->database();
        $this->ci->load->model('tank_auth/users');

        // Try to autologin
        $this->autologin();
    }

    /**
     * Login user on the site. Return TRUE if login is successful
     * (user exists and activated, password is correct), otherwise FALSE.
     *
     * @param	string	(username or email or both depending on settings in config file)
     * @param	string password
     * @param	bool remember หรือไม่
     * @return	bool TRUE โดย User จะต้อง activate แล้ว และ password ถูกต้อง เท่านั้น
     * @uses Users::get_user_by_login()
     * @uses Users::get_user_by_username()
     * @uses Users::get_user_by_email()
     * @uses PasswordHash::CheckPassword()
     * @uses create_autologin()
     * @uses clear_login_attempts()
     */
    function login($login, $password, $remember, $login_by_username, $login_by_email) {
        if ((strlen($login) > 0) AND (strlen($password) > 0)) {

            // Which function to use to login (based on config)
            //เลือกการทำงาน ว่าจะให้ทำงานที่ function ไหน
            if ($login_by_username AND $login_by_email) {
                $get_user_func = 'get_user_by_login';
            } else if ($login_by_username) {
                $get_user_func = 'get_user_by_username';
            } else {
                $get_user_func = 'get_user_by_email';
            }
            //ถ้าค้นแล้วพบ user ดังกล่าว
            if (!is_null($user = $this->ci->users->$get_user_func($login))) { // login ok
                // Does password match hash in database?
                //เช็คว่า password ผ่านการเข้ารหัสมาอย่างถูกต้อง
                $hasher = new PasswordHash(PHPASS_HASH_STRENGTH, PHPASS_HASH_PORTABLE);
                if ($hasher->CheckPassword($password, $user->password)) {  // password ok
                    //เช็คว่า user ban หรือเปล่า
                    if ($user->banned == 1) {         // fail - banned
                        $this->error = array('banned' => $user->ban_reason);
                    } else {
                        //เขียน session login
                        $this->ci->session->set_userdata(array(
                            'user_id' => $user->id,
                            'username' => $user->username,
                            'status' => ($user->activated == 1) ? STATUS_ACTIVATED : STATUS_NOT_ACTIVATED,
                        ));
                        // ถ้ายังไม่ activate
                        if ($user->activated == 0) {       // fail - not activated
                            $this->error = array('not_activated' => '');
                        } else {            // success
                            //เลือกให้ autologin ด้วยหรือเปล่า
                            if ($remember) {
                                $this->create_autologin($user->id);
                            }

                            $this->clear_login_attempts($login);

                            $this->ci->users->update_login_info(
                                    $user->id,
                                    $this->ci->config->item('login_record_ip', 'tank_auth'),
                                    $this->ci->config->item('login_record_time', 'tank_auth'));
                            return TRUE;
                        }
                    }
                } else {              // fail - wrong password
                    $this->increase_login_attempt($login);
                    $this->error = array('password' => 'auth_incorrect_password');
                }
            } else {               // fail - wrong login
                $this->increase_login_attempt($login);
                $this->error = array('login' => 'auth_incorrect_login');
            }
        }
        return FALSE;
    }

    /**
     * Logout user from the site
     *
     * @return	void
     */
    function logout() {
        $this->delete_autologin();

        // See http://codeigniter.com/forums/viewreply/662369/ as the reason for the next line
        $this->ci->session->set_userdata(array('user_id' => '', 'username' => '', 'status' => ''));

        $this->ci->session->sess_destroy();
    }

    /**
     * Check if user logged in. Also test if user is activated or not.
     * ตรวจสอบว่า user login หรือยัง รวมทั้ง ตรวจสอบว่า active แล้วหรือเปล่า
     *
     * @param	bool true คือต้อง activate แล้ว
     * @return	bool
     */
    function is_logged_in($activated = TRUE) {
        return $this->ci->session->userdata('status') === ($activated ? STATUS_ACTIVATED : STATUS_NOT_ACTIVATED);
    }

    /*
     * BeYourCyber 24/11/2010 check state for admin
     */

    function is_admin() {
        $this->ci->load->model('tank_auth/group');
        $user_group = $this->ci->group->is_admin($this->ci->session->userdata('user_id'));
        if($user_group->memgroup >= 110){
            return TRUE;
        } else {
            return FALSE;
        }
        
    }

    /**
     * Get user_id
     *
     * @return	string
     */
    function get_user_id() {
        return $this->ci->session->userdata('user_id');
    }

    /**
     * Get username
     *
     * @return	string
     */
    function get_username() {
        return $this->ci->session->userdata('username');
    }

    /**
     * Create new user on the site and return some data about it:
     * user_id, username, password, email, new_email_key (if any).
     * สร้าง user ใหม่และคืนค่าเป็นข้อมูลของสมาชิกคนนี้ ประกอบด้วย
     * user_id, username, password, email
     *
     * @param	string username
     * @param	string email
     * @param	string password
     * @param	bool ต้อง activate email ด้วยหรือไม่
     * @return	array username,email,password
     * @uses Users::is_email_available()
     */
    function create_user($username, $email, $password, $email_activation) {
        if ((strlen($username) > 0) AND !$this->ci->users->is_username_available($username)) {
            $this->error = array('username' => 'auth_username_in_use');
        } elseif (!$this->ci->users->is_email_available($email)) {
            $this->error = array('email' => 'auth_email_in_use');
        } else {
            // Hash password using phpass
            $hasher = new PasswordHash(PHPASS_HASH_STRENGTH, PHPASS_HASH_PORTABLE);
            $hashed_password = $hasher->HashPassword($password);

            $data = array(
                'username' => $username,
                'password' => $hashed_password,
                'email' => $email,
                'last_ip' => $this->ci->input->ip_address(),
            );

            if ($email_activation) {
                $data['new_email_key'] = md5(rand() . microtime());
            }
            if (!is_null($res = $this->ci->users->create_user($data, !$email_activation))) {
                $data['user_id'] = $res['user_id'];
                $data['password'] = $password;
                unset($data['last_ip']);
                return $data;
            }
        }
        return NULL;
    }

    /**
     * Check if username available for registering.
     * Can be called for instant form validation.
     * เช็คว่า username นี้สามารถใช้ลงทะเบียนได้
     *
     * @param	string username
     * @return	bool
     * @uses Users::is_username_available() Model
     */
    function is_username_available($username) {
        return ((strlen($username) > 0) AND $this->ci->users->is_username_available($username));
    }

    /**
     * Check if email available for registering.
     * Can be called for instant form validation.
     * เช็คว่า email สามารถใช้ลงทะเบียนได้
     *
     * @param	string email
     * @return	bool
     * @uses Users::is_email_available() Model
     */
    function is_email_available($email) {
        return ((strlen($email) > 0) AND $this->ci->users->is_email_available($email));
    }

    /**
     * Change email for activation and return some data about user:
     * user_id, username, email, new_email_key.
     * Can be called for not activated users only.
     * เปลี่ยน email และส่งอีเมลไปยืนยัน รวมทั้งคืนค่าที่เกี่ยวกับ user ได้แก่
     * user_id, username, email,
     * ใช้สำหรับ user ที่ยังไม่ activate เท่านั้น
     *
     * @param	string email
     * @return	array
     * @uses Users::get_user_by_id() Model
     * @uses Users::is_email_available() Model
     * @uses Users::set_new_email() Model
     */
    function change_email($email) {
        $user_id = $this->ci->session->userdata('user_id');

        if (!is_null($user = $this->ci->users->get_user_by_id($user_id, FALSE))) {

            $data = array(
                'user_id' => $user_id,
                'username' => $user->username,
                'email' => $email,
            );
            if (strtolower($user->email) == strtolower($email)) {  // leave activation key as is
                $data['new_email_key'] = $user->new_email_key;
                return $data;
            } elseif ($this->ci->users->is_email_available($email)) {
                $data['new_email_key'] = md5(rand() . microtime());
                $this->ci->users->set_new_email($user_id, $email, $data['new_email_key'], FALSE);
                return $data;
            } else {
                $this->error = array('email' => 'auth_email_in_use');
            }
        }
        return NULL;
    }

    /**
     * Activate user using given key
     * Activate user โดยใช้ key
     *
     * @param	string User id
     * @param	string Activate key
     * @param	bool Activate ด้วย email
     * @return	bool
     * @uses Users::purge_na() Model
     * @uses Users::activate_user() Model
     */
    function activate_user($user_id, $activation_key, $activate_by_email = TRUE) {
        $this->ci->users->purge_na($this->ci->config->item('email_activation_expire', 'tank_auth'));

        if ((strlen($user_id) > 0) AND (strlen($activation_key) > 0)) {
            return $this->ci->users->activate_user($user_id, $activation_key, $activate_by_email);
        }
        return FALSE;
    }

    /**
     * Set new password key for user and return some data about user:
     * user_id, username, email, new_pass_key.
     * The password key can be used to verify user when resetting his/her password.
     * สร้าง passkey ใหม่ เพื่อใช้สำหรับกรณี reset password
     *
     * @param	string username|email
     * @return	array
     * @uses Users::get_user_by_login() Model
     * @uses Users::set_password_key() Model
     */
    function forgot_password($login) {
        if (strlen($login) > 0) {
            if (!is_null($user = $this->ci->users->get_user_by_login($login))) {

                $data = array(
                    'user_id' => $user->id,
                    'username' => $user->username,
                    'email' => $user->email,
                    'new_pass_key' => md5(rand() . microtime()),
                );

                $this->ci->users->set_password_key($user->id, $data['new_pass_key']);
                return $data;
            } else {
                $this->error = array('login' => 'auth_incorrect_email_or_username');
            }
        }
        return NULL;
    }

    /**
     * Check if given password key is valid and user is authenticated.
     * เช็คว่า password key ถุกต้อง และ user authen แล้ว
     *
     * @param	string userid
     * @param	string passkey
     * @return	bool
     * @uses Users::can_reset_password() Model
     */
    function can_reset_password($user_id, $new_pass_key) {
        if ((strlen($user_id) > 0) AND (strlen($new_pass_key) > 0)) {
            return $this->ci->users->can_reset_password(
                    $user_id,
                    $new_pass_key,
                    $this->ci->config->item('forgot_password_expire', 'tank_auth'));
        }
        return FALSE;
    }

    /**
     * Replace user password (forgotten) with a new one (set by user)
     * and return some data about it: user_id, username, new_password, email.
     * เปลี่ยนรหัสผ่านใหม่ (ที่ user forgot) ด้วยรหัสที่ตั้งใหม่
     *
     *
     * @param	string user id
     * @param	string passkey
     * @param   string รหัสผ่านใหม่
     * @return	bool
     * @uses Users::get_user_by_id() Model
     * @uses PasswordHash::HashPassword() Library
     * @uses Users::reset_password() Model
     * @uses User_Autologin::clear() Model
     */
    function reset_password($user_id, $new_pass_key, $new_password) {
        if ((strlen($user_id) > 0) AND (strlen($new_pass_key) > 0) AND (strlen($new_password) > 0)) {

            if (!is_null($user = $this->ci->users->get_user_by_id($user_id, TRUE))) {

                // Hash password using phpass
                $hasher = new PasswordHash(PHPASS_HASH_STRENGTH, PHPASS_HASH_PORTABLE);
                $hashed_password = $hasher->HashPassword($new_password);

                if ($this->ci->users->reset_password(
                                $user_id,
                                $hashed_password,
                                $new_pass_key,
                                $this->ci->config->item('forgot_password_expire', 'tank_auth'))) { // success
                    // Clear all user's autologins
                    $this->ci->load->model('tank_auth/user_autologin');
                    $this->ci->user_autologin->clear($user->id);

                    return array(
                        'user_id' => $user_id,
                        'username' => $user->username,
                        'email' => $user->email,
                        'new_password' => $new_password,
                    );
                }
            }
        }
        return NULL;
    }

    /**
     * Change user password (only when user is logged in)
     * เปลี่ยนรหัสผ่าน (กรณีที่ user login อยู่เท่านั้น)
     *
     * @param	string รหัสผ่านปัจจุบัน
     * @param	string รหัสผ่านใหม่
     * @return	bool สถานะเปลี่ยนรหัสได้หรือไม่
     * @uses Users::get_user_by_id() Model
     * @uses PasswordHash::HashPassword() Library
     * @uses PasswordHash::CheckPassword() Library
     * @uses Users::change_password() Model
     */
    function change_password($old_pass, $new_pass) {
        $user_id = $this->ci->session->userdata('user_id');

        if (!is_null($user = $this->ci->users->get_user_by_id($user_id, TRUE))) {

            // Check if old password correct
            $hasher = new PasswordHash(PHPASS_HASH_STRENGTH, PHPASS_HASH_PORTABLE);
            if ($hasher->CheckPassword($old_pass, $user->password)) {   // success
                // Hash new password using phpass
                $hashed_password = $hasher->HashPassword($new_pass);

                // Replace old password with new one
                $this->ci->users->change_password($user_id, $hashed_password);
                return TRUE;
            } else {               // fail
                $this->error = array('old_password' => 'auth_incorrect_password');
            }
        }
        return FALSE;
    }

    /**
     * Change user email (only when user is logged in) and return some data about user:
     * user_id, username, new_email, new_email_key.
     * The new email cannot be used for login or notification before it is activated.
     *
     * เปลี่ยน email user กรณีที่ login อยู่เท่านั้น
     * email ใหม่นี้จะไม่สามารถใช้ได้จนกว่าจะ Activate
     *
     * @param	string email ใหม่
     * @param	string รหัสผ่านปัจจุบัน
     * @return	array user_id, username, new_email, new_email_key.
     * @uses Users::get_user_by_id() Model
     * @uses PasswordHash::CheckPassword() Library
     * @uses Users::is_email_available() Model
     */
    function set_new_email($new_email, $password) {
        $user_id = $this->ci->session->userdata('user_id');

        if (!is_null($user = $this->ci->users->get_user_by_id($user_id, TRUE))) {

            // Check if password correct
            $hasher = new PasswordHash(PHPASS_HASH_STRENGTH, PHPASS_HASH_PORTABLE);
            if ($hasher->CheckPassword($password, $user->password)) {   // success
                $data = array(
                    'user_id' => $user_id,
                    'username' => $user->username,
                    'new_email' => $new_email,
                );

                if ($user->email == $new_email) {
                    $this->error = array('email' => 'auth_current_email');
                } elseif ($user->new_email == $new_email) {  // leave email key as is
                    $data['new_email_key'] = $user->new_email_key;
                    return $data;
                } elseif ($this->ci->users->is_email_available($new_email)) {
                    $data['new_email_key'] = md5(rand() . microtime());
                    $this->ci->users->set_new_email($user_id, $new_email, $data['new_email_key'], TRUE);
                    return $data;
                } else {
                    $this->error = array('email' => 'auth_email_in_use');
                }
            } else {               // fail
                $this->error = array('password' => 'auth_incorrect_password');
            }
        }
        return NULL;
    }

    /**
     * Activate new email, if email activation key is valid.
     * Activate mail ใหม่ถ้า key ถูกต้อง
     *
     * @param	string user id
     * @param	string email key
     * @return	bool
     * @uses Users::activate_new_email() Model
     */
    function activate_new_email($user_id, $new_email_key) {
        if ((strlen($user_id) > 0) AND (strlen($new_email_key) > 0)) {
            return $this->ci->users->activate_new_email(
                    $user_id,
                    $new_email_key);
        }
        return FALSE;
    }

    /**
     * Delete user from the site (only when user is logged in)
     * ลบ user ออก เฉพาะกรณี login อยู่เท่านั้น
     *
     * @param	string รหัสผ่าน
     * @return	bool
     * @uses PasswordHash::CheckPassword() Library
     * @uses Users::delete_user() Model
     */
    function delete_user($password) {
        $user_id = $this->ci->session->userdata('user_id');

        if (!is_null($user = $this->ci->users->get_user_by_id($user_id, TRUE))) {

            // Check if password correct
            $hasher = new PasswordHash(PHPASS_HASH_STRENGTH, PHPASS_HASH_PORTABLE);
            if ($hasher->CheckPassword($password, $user->password)) {   // success
                $this->ci->users->delete_user($user_id);
                $this->logout();
                return TRUE;
            } else {               // fail
                $this->error = array('password' => 'auth_incorrect_password');
            }
        }
        return FALSE;
    }

    /**
     * Get error message.
     * Can be invoked after any failed operation such as login or register.
     *
     * เรียก error ไปแสดงในกรณีที่มี error เกิดขึ้นในระบบ
     *
     * @return	string
     */
    function get_error_message() {
        return $this->error;
    }

    /**
     * Save data for user's autologin
     * บันทึกค่ากรณีที่ user ทำการ autologin
     *
     * @param	int userid
     * @return	bool
     */
    private function create_autologin($user_id) {
        //สร้าง cookie ใส่ใน $key ยาว 16 ตัวอักษร
        $this->ci->load->helper('cookie');
        $key = substr(md5(uniqid(rand() . get_cookie($this->ci->config->item('sess_cookie_name')))), 0, 16);

        $this->ci->load->model('tank_auth/user_autologin');
        $this->ci->user_autologin->purge($user_id);
        //ตั้งค่า userid และ key
        if ($this->ci->user_autologin->set($user_id, md5($key))) {
            //เขียน cookie key
            set_cookie(array(
                'name' => $this->ci->config->item('autologin_cookie_name', 'tank_auth'),
                'value' => serialize(array('user_id' => $user_id, 'key' => $key)),
                'expire' => $this->ci->config->item('autologin_cookie_life', 'tank_auth'),
            ));
            return TRUE;
        }
        return FALSE;
    }

    /**
     * Clear user's autologin data
     *
     * @return	void
     */
    private function delete_autologin() {
        $this->ci->load->helper('cookie');
        if ($cookie = get_cookie($this->ci->config->item('autologin_cookie_name', 'tank_auth'), TRUE)) {

            $data = unserialize($cookie);

            $this->ci->load->model('tank_auth/user_autologin');
            $this->ci->user_autologin->delete($data['user_id'], md5($data['key']));

            delete_cookie($this->ci->config->item('autologin_cookie_name', 'tank_auth'));
        }
    }

    /**
     * Login user automatically if he/she provides correct autologin verification
     * login เข้าระบบอัตโนมัติ ถ้า user มี autologin verification ที่ถูกต้อง
     * @return	void
     * @uses Users::update_login_info()
     * @uses User_Autologin::get()
     */
    private function autologin() {
        if (!$this->is_logged_in() AND !$this->is_logged_in(FALSE)) {   // not logged in (as any user)
            $this->ci->load->helper('cookie');
            // เรียก cookie ขึ้นมา (ชื่อ cookie เก็บอยู่ใน autologin_cookie_name config)
            if ($cookie = get_cookie($this->ci->config->item('autologin_cookie_name', 'tank_auth'), TRUE)) {
                // มี cookie ตัวนี้
                // ทำการ unserialize cookie ที่เก็บขึ้นมา
                $data = unserialize($cookie);
                //เช็คว่า มี key และ มี userid
                if (isset($data['key']) AND isset($data['user_id'])) {
                    //load model user_autologin เพื่อเข้าส่วนการทำงาน autologin model
                    $this->ci->load->model('tank_auth/user_autologin');
                    //เช็คว่า มี user และ key นี้จริง
                    if (!is_null($user = $this->ci->user_autologin->get($data['user_id'], md5($data['key'])))) {

                        // Login user
                        $this->ci->session->set_userdata(array(
                            'user_id' => $user->id,
                            'username' => $user->username,
                            'status' => STATUS_ACTIVATED,
                        ));

                        // Renew users cookie to prevent it from expiring
                        set_cookie(array(
                            'name' => $this->ci->config->item('autologin_cookie_name', 'tank_auth'),
                            'value' => $cookie,
                            'expire' => $this->ci->config->item('autologin_cookie_life', 'tank_auth'),
                        ));
                        //ปรับข้อมูลการ login ครั้งล่าสุด
                        $this->ci->users->update_login_info(
                                $user->id,
                                $this->ci->config->item('login_record_ip', 'tank_auth'),
                                $this->ci->config->item('login_record_time', 'tank_auth'));
                        return TRUE;
                    }
                }
            }
        }
        return FALSE;
    }

    /**
     * Check if login attempts exceeded max login attempts (specified in config)
     * เช็คว่า user ได้ทำการ login เกินกว่าค่าที่กำหนดหรือยัง
     *
     * @param	string username|password
     * @return	bool
     * @uses Config::tank_auth Configuration
     * @uses Login_attempts::get_attempts_num() Model
     */
    function is_max_login_attempts_exceeded($login) {
        if ($this->ci->config->item('login_count_attempts', 'tank_auth')) {
            $this->ci->load->model('tank_auth/login_attempts');
            return $this->ci->login_attempts->get_attempts_num($this->ci->input->ip_address(), $login)
            >= $this->ci->config->item('login_max_attempts', 'tank_auth');
        }
        return FALSE;
    }

    /**
     * Increase number of attempts for given IP-address and login
     * (if attempts to login is being counted)
     *
     * เพิ่ม login attemp โดยใช้ user|email และ ip
     *
     * @param	string Username|email
     * @return	void
     * @uses is_max_login_attempts_exceeded() Self
     * @uses Login_attempts::increase_attempt() Model
     */
    private function increase_login_attempt($login) {
        if ($this->ci->config->item('login_count_attempts', 'tank_auth')) {
            if (!$this->is_max_login_attempts_exceeded($login)) {
                $this->ci->load->model('tank_auth/login_attempts');
                $this->ci->login_attempts->increase_attempt($this->ci->input->ip_address(), $login);
            }
        }
    }

    /**
     * Clear all attempt records for given IP-address and login
     * (if attempts to login is being counted)
     *
     * @param	string
     * @return	void
     */
    private function clear_login_attempts($login) {
        if ($this->ci->config->item('login_count_attempts', 'tank_auth')) {
            $this->ci->load->model('tank_auth/login_attempts');
            $this->ci->login_attempts->clear_attempts(
                    $this->ci->input->ip_address(),
                    $login,
                    $this->ci->config->item('login_attempt_expire', 'tank_auth'));
        }
    }

}

/* End of file Tank_auth.php */
/* Location: ./application/libraries/Tank_auth.php */